package com.kaibes.orm.core.core;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import com.kaibes.core.base.util.ListUtils;
import com.kaibes.orm.core.dialect.Dialect;
import com.kaibes.orm.core.sql.DeleteSql;
import com.kaibes.orm.core.sql.InsertSql;
import com.kaibes.orm.core.sql.SelectSql;
import com.kaibes.orm.core.sql.UpdateSql;
import com.kaibes.orm.core.util.ColumnUtils;
import com.kaibes.orm.core.util.TableUtils;
import com.kaibes.orm.curd.Delete;
import com.kaibes.orm.curd.Insert;
import com.kaibes.orm.curd.Select;
import com.kaibes.orm.curd.SelectBuilder;
import com.kaibes.orm.curd.Update;

public abstract class TableParser<E> {

    protected String tableName;
    private Class<E> entityClass;
    private Dialect dialect;
    private SqlRunner sqlRunner;
    
    private TableColumn entityColumn;
    private ConcurrentHashMap<Class<?>, TableColumn> tableColumnMap;
    
    public TableParser(Dialect dialect, SqlRunner sqlRunner) {
        if (dialect != null && sqlRunner != null) {
            init(dialect, sqlRunner);
        }
    }
    
    public String getTableName() {
        return tableName;
    }
    
    protected void init(Dialect dialect, SqlRunner sqlRunner) {
        this.dialect = dialect;
        this.sqlRunner = sqlRunner;
        tableColumnMap = new ConcurrentHashMap<>();
    }
    
    public final void setEntityClass(Class<E> entityClass) {
        if (entityClass != null) {
            this.entityClass = entityClass;
            this.tableName = TableUtils.getTableName(entityClass);
            entityColumn = getTableColumn(entityClass);
        }
    }
    
    public Class<E> getEntityClass() {
        return entityClass;
    }
    
    public TableColumn getTableColumn() {
        return entityColumn;
    }
    
    public TableColumn getTableColumn(Class<?> clazz) {
        TableColumn tableColumn = tableColumnMap.get(clazz);
        if (tableColumn == null) {
            tableColumn = ColumnUtils.getTableColumn(clazz);
            tableColumnMap.put(clazz, tableColumn);
        }
        return tableColumn;
    }
    
    // ============
    private InsertSql getInsertSql(Insert insert) {
        if (ListUtils.isEmpty(insert.getDatas())) {
            return null;
        }
        Class<? extends Object> clazz = insert.getDatas().get(0).getClass();
        TableColumn tableColumn = getTableColumn(clazz);
        if (tableColumn.getSql() != null) {
            InsertSql insertSql = new InsertSql();
            insertSql.setSql(tableColumn.getSql());
            insertSql.setDataList(insert.getDatas());
            insertSql.setTableColumn(tableColumn);
            insertSql.setIdField(entityColumn.getIdColumn());
            return insertSql;
        } else {
            InsertSql insertSql = dialect.createInsertSql(insert);
            tableColumn.setSql(insertSql.getSql());
            insertSql.setTableColumn(tableColumn);
            insertSql.setIdField(entityColumn.getIdColumn());
            return insertSql;
        }
    }

    private DeleteSql getDeleteSql(Delete delete) {
        if (ListUtils.isEmpty(delete.getQueries())) {
            return null;
        }
        return dialect.createDeleteSql(delete);
    }

    private UpdateSql getUpdateSql(Update update) {
        if (ListUtils.isEmpty(update.getQueries())) {
            return null;
        }
        return dialect.createUpdateSql(update);
    }

    private SelectSql getSelectSql(Select select, Class<?> targetClass) {
        SelectSql selectSql = null;
        selectSql = dialect.createSelectSql(select);
        if (!select.isCounted()) {
            selectSql.setTableColumn(getTableColumn(targetClass));
        }
        return selectSql;
    }

    // ===============
    public <T> List<T> insert(Insert insert) {
        return sqlRunner.insert(getInsertSql(insert));
    }

    public int delete(Delete delete) {
        return sqlRunner.delete(getDeleteSql(delete));
    }

    public int update(Update update) {
        return sqlRunner.update(getUpdateSql(update));
    }

    public <T> List<T> select(Select select, Class<T> targetClass) {
        return sqlRunner.select(getSelectSql(select, targetClass));
    }

    public final List<E> select(Select select) {
        return select(select.setCounted(false), entityClass);
    }

    public final long count() {
        return count(SelectBuilder.newBuilder().build());
    }

    public final long count(Select select) {
        return select(select.setCounted(true), Long.class).get(0);
    }

    public final <T> T selectFirst(Select select, Class<T> clazz) {
        select.setSize(1);
        List<T> dataList = select(select, clazz);
        if (dataList == null || dataList.isEmpty()) {
            return null;
        } else {
            return dataList.get(0);
        }
    }

    public final E selectFirst(Select select) {
        return selectFirst(select, entityClass);
    }

}
